package org.dpes.apriori;

import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.dpes.apriori.Elementy.Element;
import org.dpes.apriori.Krotki.Krotka;



public class ZbioryCzeste {

	private final Map<Element, IndeksBitowy> indeks = new HashMap<Element, IndeksBitowy>();

	public ZbioryCzeste(Wyniki wyniki) {
		tworzIndeks(wyniki.getWyniki("wyniki.dat"));
	}

	private void tworzIndeks(int[][] wyniki) {
		for (int i = 0; i < Wyniki.losowan; i++) {
			for (int j = 0; j < Wyniki.wybranych; j++) {

				int wartosc = wyniki[i][j];
				int numerWiersza = i + 1;
				
				Element element = Elementy.getSingleton().podajElement(wartosc);
				Krotka krotka = Krotki.getSingleton()
						.podajElement(numerWiersza);
				
				if (indeks.containsKey(element)) {
					indeks.get(element).setUpRow(krotka);
				} else {
					IndeksBitowy ib = new IndeksBitowy(Wyniki.losowan);
					ib.setUpRow(krotka);
					indeks.put(element, ib);
				}
			}
		}
	}

	private List<Zbior> listaPojedynczych = new LinkedList<Zbior>();

	private int liczbaPowtorzen;

	private ZbiorStorage current;

	public void wyswietlZbioryCzeste(int licznoscZbioru, int liczbaPowtorzen) {

		this.liczbaPowtorzen = liczbaPowtorzen;

		tworzZbioryPoczatkowe();

		current = new ZbiorStorage(2000);

		Iterator<Zbior> it = listaPojedynczych.iterator();
		while (it.hasNext()) {
			Zbior zbior = it.next();
			current.add(zbior);
		}

		generujZbioryCzeste(2, 2000000);
		generujZbioryCzeste(3, 2000000);
		generujZbioryCzeste(4, 20000000);
		generujZbioryCzeste(5, 20000000);
		generujZbioryCzeste(6, 30000000);
		generujZbioryCzeste(7, 30000000);
		generujZbioryCzeste(8, 30000000);
		generujZbioryCzeste(9, 30000000);
		generujZbioryCzeste(10, 30000000);
		printToFile("10.txt");
		generujZbioryCzeste(11, 30000000);
		printToFile("11.txt");
		generujZbioryCzeste(12, 30000000);
		printToFile("12.txt");
		generujZbioryCzeste(13, 30000000);
		printToFile("13.txt");
		generujZbioryCzeste(14, 30000000);
		printToFile("14.txt");
		generujZbioryCzeste(15, 30000000);
		printToFile("15.txt");
		generujZbioryCzeste(16, 30000000);
		printToFile("16.txt");
		generujZbioryCzeste(17, 30000000);
		printToFile("17.txt");
		generujZbioryCzeste(18, 30000000);
		printToFile("18.txt");
		generujZbioryCzeste(19, 30000000);
		printToFile("19.txt");

		/*
		 * Mon Nov 26 10:53:52 CET 2007
		 * wygenerowalem licznosc 2 dodalem 3160
		 * wygenerowalem licznosc 3 dodalem 82160
		 * wygenerowalem licznosc 4 dodalem 1581407
		 * wygenerowalem licznosc 5 dodalem 16996472
		 * wygenerowalem licznosc 6 dodalem 26705005
		 * Mon Nov 26 12:28:17 CET 2007
		 */
	}

	private void printToFile(String string) {
		try {
			FileWriter fw = new FileWriter(string);
			
			Iterator <Zbior>it = current.iterator();
			
			while (it.hasNext()) {
				Zbior zbior = it.next();
				Iterator <Element>elementy = zbior.podajElementy().iterator();
				String ele = "";
				while (elementy.hasNext()) {
					Elementy.Element element =  elementy.next();
					ele += element+"-";
				}
				fw.write(ele.substring(0, ele.length()-1) + "," + podajCzestosc(zbior)+"\n");
			}
			fw.close();
		
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	private void generujZbioryCzeste(int aktualnaLicznosc, int size) {

		ZbiorStorage wlasnieDodane = new ZbiorStorage(size);

		HashTab kodyObecnych = new HashTab(size);

		Iterator<Zbior> generowane = current.iterator();

		int counter = 0;

		while (generowane.hasNext()) {
			Zbior generator = generowane.next();
			Iterator<Zbior> pojedyncze = listaPojedynczych.iterator();

			counter++;
			if (counter % 10000 == 0) {
				String wielkosci = String.format(" (%8d:%8d)", kodyObecnych
						.size(), wlasnieDodane.size());
				printProgress("generuje licznosc " + aktualnaLicznosc
						+ wielkosci, current.size(), counter);
			}

			while (pojedyncze.hasNext()) {
				Zbior pojedynczy = pojedyncze.next();

				Zbior tymczasowePolaczenie = pojedynczy.sklejZbiory(generator);

				if (tymczasowePolaczenie.podajLicznoscZbioru() == aktualnaLicznosc
						&& !kodyObecnych.isThere(tymczasowePolaczenie.hash())) {

					if (czyCzesty(tymczasowePolaczenie)) {
						wlasnieDodane.add(tymczasowePolaczenie);
						kodyObecnych.insert(tymczasowePolaczenie.hash());
					}
				}
			}
		}

		System.out.println("wygenerowalem licznosc " + aktualnaLicznosc
				+ " dodalem " + wlasnieDodane.size()+"                                      ");

		current = wlasnieDodane;
	}

	private boolean czyCzesty(Zbior zbior) {

		Iterator<Element> it = zbior.podajElementy().iterator();

		Element element = it.next();

		IndeksBitowy przeciecie = new IndeksBitowy(indeks.get(element));

		while (it.hasNext()) {
			Element prawy = it.next();
			przeciecie.join(indeks.get(prawy));
		}

		if (przeciecie.countRows() < liczbaPowtorzen) {
			return false;
		} else {
			return true;
		}
	}
	
	private int podajCzestosc(Zbior zbior) {

		Iterator<Element> it = zbior.podajElementy().iterator();

		Element element = it.next();

		IndeksBitowy przeciecie = new IndeksBitowy(indeks.get(element));

		while (it.hasNext()) {
			Element prawy = it.next();
			przeciecie.join(indeks.get(prawy));
		}
		
		return przeciecie.countRows();
	}

	private void tworzZbioryPoczatkowe() {
		Iterator<Element> it = indeks.keySet().iterator();
		while (it.hasNext()) {
			Element klucz = it.next();
			if (indeks.get(klucz).countRows() >= liczbaPowtorzen) {
				Zbior zbior = new ZbiorPojedynczy();
				zbior.dodajElement(klucz);
				listaPojedynczych.add(zbior);
			}
		}
	}

	public static void printProgress(String preStr, int total, int current) {
		int totalLen = 20;
		int currentLen = (int) (((float) current / (float) total) * totalLen);

		System.out.print(" " + preStr + " ");

		System.out.print("[");
		for (int i = 0; i < totalLen; i++) {
			if (i < currentLen)
				System.out.print("+");
			else
				System.out.print("-");
		}
		System.out.print("]");

		System.out.print("\r");
	}
}
